#include <memory>
#include <vector>
#include <optional>

template <typename T>
class Grid
{
public:
    explicit Grid(size_t width = DefaultWidth, size_t height = DefaultHeight)
        : m_width{width},
          m_height{height}
    {
        m_cells.resize(m_width);
        for (auto &column : m_cells)
        {
            column.resize(m_height);
        }
    }

    virtual ~Grid() = default;

    Grid(const Grid &src) = default;
    Grid &operator=(const Grid &rhs) = default;

    // Explicitly default a move constructor and assignment operator.
    Grid(Grid &&src) = default;
    Grid &operator=(Grid &&rhs) = default;

    std::optional<T> &at(size_t x, size_t y)
    {
        verifyCoordinate(x, y);
        return m_cells[x][y];
    }

    const std::optional<T> &at(size_t x, size_t y) const
    {
        return const_cast<std::optional<T> &>(std::as_const(*this).at(x, y));
    }

    size_t getHeight() const
    {
        return m_height;
    }

    size_t getWidth() const
    {
        return m_width;
    }

    static const size_t DefaultWidth{10};
    static const size_t DefaultHeight{10};

    //
    void draw()
    {
        for (size_t i{0}; i < m_width; ++i)
        {
            for (size_t j{0}; j < m_height; ++j)
            {
                std::cout << std::format(
                    "{}({}*{}) ", m_cells[i][j] ? 1 : 0, i, j);
            }
            std::cout << std::endl;
        }
    }

private:
    void verifyCoordinate(size_t x, size_t y) const
    {
        if (x >= m_width)
        {
            throw std::out_of_range{std::format("{} must be less than {}.", x, m_width)};
        }

        if (y >= m_height)
        {
            throw std::out_of_range{std::format("{} must be less than {}.", y, m_height)};
        }
    }

    std::vector<std::vector<std::optional<T>>> m_cells;
    size_t m_width;
    size_t m_height;
};